package org.xm.sk.common;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidParameterSpecException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;

import org.xm.sk.vo.Base;
import org.xm.sk.vo.e.Datalevel;
import org.xm.sk.vo.ob.Local;

public class ToolsUtil {

    /**
     * 对字符串进行BASE64加密
     * 
     * @param input
     *            待加密字节串
     * @return String
     * @throws Exception
     */
    public static String encodeBase64(byte[] input) throws Exception {
        Class<?> clazz = Class.forName("sun.misc.BASE64Encoder");
        Method mainMethod = clazz.getMethod("encode", byte[].class);
        mainMethod.setAccessible(true);
        Object retObj = mainMethod.invoke(clazz.newInstance(),
                new Object[] { input });
        return (String) retObj;
    }

    /**
     * 对字符串进行BASE64加密
     * 
     * @param str
     *            待加密字符串
     * @return byte[]
     * @throws Exception
     */
    public static byte[] decodeBase64(String input) throws Exception {
        Class<?> clazz = Class.forName("sun.misc.BASE64Decoder");
        Method mainMethod = clazz.getMethod("decodeBuffer", String.class);
        mainMethod.setAccessible(true);
        Object retObj = mainMethod.invoke(clazz.newInstance(), input);
        return (byte[]) retObj;
    }

    /**
     * 对字符串内容作DES加密
     * 
     * @param str
     *            要加密的字符串
     * @return String
     * @throws Exception
     */
    public static String desdecrypt(String str) throws Exception {
        String key = ComParam.PASSWORD_KEY;
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        DESKeySpec dks = new DESKeySpec(key.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(dks);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.ENCRYPT_MODE, securekey, sr);
        return encodeBase64(cipher.doFinal(str.getBytes("UTF-8")));
    }

    /**
     * 对字符串内容作DES解密
     * 
     * @param str
     *            要解密的字符串
     * @param key
     *            密钥
     * @return String
     * @throws Exception
     */
    public static String desencrypt(String str) throws Exception {
        String key = ComParam.PASSWORD_KEY;
        SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
        DESKeySpec dks = new DESKeySpec(key.getBytes());
        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
        SecretKey securekey = keyFactory.generateSecret(dks);
        Cipher cipher = Cipher.getInstance("DES");
        cipher.init(Cipher.DECRYPT_MODE, securekey, sr);
        byte[] buf = decodeBase64(str);
        return new String(cipher.doFinal(buf));
    }

    /**
     * 对字符串内容作AES加密
     * 
     * @param key
     *            密钥
     * @param iv
     *            向量
     * @param content
     *            要加密的字节串
     * @return byte[]
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws UnsupportedEncodingException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidParameterSpecException 
     */
    public static byte[] aesencrypt(byte[] key, byte[] iv, byte[] content)
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            UnsupportedEncodingException, InvalidKeyException,
            IllegalBlockSizeException, BadPaddingException,
            InvalidAlgorithmParameterException, InvalidParameterSpecException {
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
        return cipher.doFinal(content);
    }

    /**
     * 对字符串内容作AES解密
     * 
     * @param key
     *            密钥
     * @param iv
     *            向量
     * @param content
     *            要解密的字节串
     * @return byte[]
     * @throws NoSuchAlgorithmException
     * @throws NoSuchPaddingException
     * @throws InvalidKeyException
     * @throws BadPaddingException
     * @throws IllegalBlockSizeException
     * @throws InvalidAlgorithmParameterException
     * @throws InvalidParameterSpecException 
     */
    public static byte[] asedecrypt(byte[] key, byte[] iv, byte[] content)
            throws NoSuchAlgorithmException, NoSuchPaddingException,
            InvalidKeyException, IllegalBlockSizeException, BadPaddingException,
            InvalidAlgorithmParameterException, InvalidParameterSpecException {
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
        return cipher.doFinal(content);
    }

    /**
     * 计算地图坐标两点距离，返回公里数
     * 
     * @param loacl1
     * @param local2
     * @return
     */
    public static double distance(Local loacl1, Local local2) {
        return Math.sqrt((((loacl1.getLongitude() - local2.getLongitude())
                * Math.PI * 12656
                * Math.cos(((loacl1.getLatitude() + local2.getLatitude()) / 2)
                        * Math.PI / 180)
                / 180)
                * ((loacl1.getLongitude() - local2.getLongitude()) * Math.PI
                        * 12656
                        * Math.cos(
                                ((loacl1.getLatitude() + local2.getLatitude())
                                        / 2) * Math.PI / 180)
                        / 180))
                + (((loacl1.getLatitude() - local2.getLatitude()) * Math.PI
                        * 12656 / 180)
                        * ((loacl1.getLatitude() - local2.getLatitude())
                                * Math.PI * 12656 / 180)));
    }

    /**
     * 获得用户真实IP
     * 
     * @param request
     *            请求对象
     * @return 真实IP地址
     */
    public static String getClientIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    public static String getIpAddr() {
        try {
            return InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            return "10.10.10.10";
        }
    }

    public static void cleanDao4Query(Base base) {
        base.setDatalevel(Datalevel.NORMAL);
        base.setCreatedate(null);
        base.setModifydate(null);
        base.setCreator(null);
        base.setEditer(null);
        base.setCreatorName(null);
        base.setEditerName(null);
        setObjectFieldsEmpty(base);
    }

    /**
     * 清理Dao对象内所有属性，父类属性清除不了
     * 
     * @param obj
     */
    public static void setObjectFieldsEmpty(Object obj) {
        Class<?> objClass = obj.getClass();
        Method[] objmethods = objClass.getDeclaredMethods();
        Map<String, Method> objMeMap = new HashMap<String, Method>();
        for (int i = 0; i < objmethods.length; i++) {
            Method method = objmethods[i];
            objMeMap.put(method.getName(), method);
        }
        for (int i = 0; i < objmethods.length; i++) {
            {
                String methodName = objmethods[i].getName();
                if (methodName != null && methodName.startsWith("get")) {
                    try {
                        Object returnObj = objmethods[i].invoke(obj,
                                new Object[0]);
                        Method setmethod = (Method) objMeMap
                                .get("set" + methodName.split("get")[1]);
                        if (returnObj != null) {
                            returnObj = null;
                        }
                        setmethod.invoke(obj, returnObj);
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 判断字符串内容是否为合法的整型数
     * 
     * @param str
     * @return
     */
    public static boolean isInteger(String str) {
        Pattern pattern = Pattern.compile("^([1-9]\\d*|0)$");
        return pattern.matcher(str).matches();
    }

    /**
     * 判断字符串内容是否为合法的数字
     * 
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("^([1-9]\\d*|0)(\\.\\d+)*$");
        return pattern.matcher(str).matches();
    }

    /**
     * (1)能匹配的年月日类型有： 2014年4月19日 2014年4月19号 2014-4-19 2014/4/19 2014.4.19
     * (2)能匹配的时分秒类型有： 15:28:21 15:28 5:28 pm 15点28分21秒 15点28分 15点
     * (3)能匹配的年月日时分秒类型有： (1)和(2)的任意组合，二者中间可有任意多个空格
     * 如果dateStr中有多个时间串存在，只会匹配第一个串，其他的串忽略
     * 
     * @param text
     * @return
     */
    public static String matchDateString(String dateStr) {
        try {
            List<String> matches = null;
            Pattern p = Pattern.compile(
                    "(\\d{1,4}[-|\\/|年|\\.]\\d{1,2}[-|\\/|月|\\.]\\d{1,2}([日|号])?(\\s)*(\\d{1,2}([点|时])?((:)?\\d{1,2}(分)?((:)?\\d{1,2}(秒)?)?)?)?(\\s)*(PM|AM)?)",
                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
            Matcher matcher = p.matcher(dateStr);
            if (matcher.find() && matcher.groupCount() >= 1) {
                matches = new ArrayList<String>();
                for (int i = 1; i <= matcher.groupCount(); i++) {
                    String temp = matcher.group(i);
                    matches.add(temp);
                }
            } else {
                matches = Collections.emptyList();
            }
            if (matches.size() > 0) {
                return ((String) matches.get(0)).trim();
            } else {
            }
        } catch (Exception e) {
            return "";
        }
        return "";
    }
}
